package net.xdclass.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jdk.security.jarsigner.JarSigner;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.component.PayFactory;
import net.xdclass.config.RabbitMQConfig;
import net.xdclass.constant.TimeConstant;
import net.xdclass.enums.*;
import net.xdclass.exception.BizException;
import net.xdclass.feign.CouponFeignService;
import net.xdclass.feign.ProductFeignService;
import net.xdclass.feign.UserFeignService;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.mapper.ProductOrderItemMapper;
import net.xdclass.mapper.ProductOrderMapper;
import net.xdclass.model.LoginUser;
import net.xdclass.model.OrderMessage;
import net.xdclass.model.ProductOrderDO;
import net.xdclass.model.ProductOrderItemDO;
import net.xdclass.request.*;
import net.xdclass.service.ProductOrderService;
import net.xdclass.util.CommonUtil;
import net.xdclass.util.JsonData;
import net.xdclass.vo.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.nio.file.LinkOption;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 小滴课堂,愿景：让技术不再难学
 *
 * @Description
 * @Author 二当家小D
 * @Remark 有问题直接联系我，源码-笔记-技术交流群
 * @Version 1.0
 **/

@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {

    @Autowired
    private ProductOrderMapper productOrderMapper;

    @Autowired
    private UserFeignService userFeignService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private ProductOrderItemMapper productOrderItemMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private PayFactory payFactory;

    /**
     * * 防重提交
     * * 用户微服务-确认收货地址
     * * 商品微服务-获取最新购物项和价格
     * * 订单验价
     * * 优惠券微服务-获取优惠券
     * * 验证价格
     * * 锁定优惠券
     * * 锁定商品库存
     * * 创建订单对象
     * * 创建子订单对象
     * * 发送延迟消息-用于自动关单
     * * 创建支付信息-对接三方支付
     *
     * @param orderRequest
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {

        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);

        //获取收获地址详情
        ProductOrderAddressVO addressVO = this.getUserAddress(orderRequest.getAddressId());
        log.info("收获地址:{}",addressVO);

        //获取用户商品信息
        List<Long> productIdList = orderRequest.getProductIdList();
        JsonData cartItemData = productFeignService.confirmOrderCartItems(productIdList);

        List<OrderItemVO> orderItemVOList = cartItemData.getData(new TypeReference<>(){});
        log.info("获取商品:{}",orderItemVOList);
        if (orderItemVOList == null){
            //购物车商品不存在
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_CART_ITEM_NOT_EXIST);
        }

        //验证价格，减去商品优惠群
        this.checkPrice(orderItemVOList,orderRequest);

        //锁定优惠券，库存
        this.lockCouponRecords(orderRequest,orderOutTradeNo);

        //锁定库存
        this.lockProductStocks(orderItemVOList,orderOutTradeNo);

        //创建订单
        ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest,loginUser,orderOutTradeNo,addressVO);

        //创建订单项
        this.saveProductOrderItems(orderOutTradeNo,productOrderDO.getId(),orderItemVOList);

        //发送延迟消息,用于自动关单
        OrderMessage orderMessage = new OrderMessage();
        orderMessage.setOutTradeNo(orderOutTradeNo);
        rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(),rabbitMQConfig.getOrderCloseDelayRoutingKey(),orderMessage);

        //创建支付 TODO
        PayInfoVo payInfoVo = new PayInfoVo();
        payInfoVo.setOutTradeNo(orderOutTradeNo);
        payInfoVo.setPayFee(productOrderDO.getPayAmount());
        payInfoVo.setClientType(orderRequest.getClientType());
        payInfoVo.setPayType(orderRequest.getPayType());

        payInfoVo.setTitle(orderItemVOList.get(0).getProductTitle());
        payInfoVo.setDescription("");

        payInfoVo.setOrderPayTimeoutMills(TimeConstant.ORDER_PAY_TIMEOUT_MILLS);
        String payResult = payFactory.pay(payInfoVo);
        if (StringUtils.isNotBlank(payResult)){
            return JsonData.buildSuccess(payResult);
        } else {
            log.error("创建支付订单失败：payResult:{},payInfo:{}",payResult,payInfoVo);
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
        }

    }

    /**
     * 新增订单项
     * @param orderOutTradeNo
     * @param orderId
     * @param orderItemVOList
     */
    private void saveProductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemVOList) {
        List<ProductOrderItemDO> list = orderItemVOList.stream().map(obj->{
            ProductOrderItemDO productOrderItemDO = new ProductOrderItemDO();

            productOrderItemDO.setProductId(obj.getProductId());
            productOrderItemDO.setBuyNum(obj.getBuyNum());
            productOrderItemDO.setOutTradeNo(orderOutTradeNo);

            productOrderItemDO.setCreateTime(new Date());

            productOrderItemDO.setProductImg(obj.getProductImg());
            productOrderItemDO.setProductName(obj.getProductTitle());

            //价格
            productOrderItemDO.setAmount(obj.getAmount());
            productOrderItemDO.setTotalAmount(obj.getTotalAmount());

            productOrderItemDO.setProductOrderId(orderId);
            return productOrderItemDO;
        }).collect(Collectors.toList());

        productOrderItemMapper.insertBatch(list);

    }


    /**
     * 创建订单
     * @param orderRequest
     * @param loginUser
     * @param orderOutTradeNo
     * @param addressVO
     */
    private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String orderOutTradeNo, ProductOrderAddressVO addressVO) {

        //用户信息
        ProductOrderDO productOrderDO = new ProductOrderDO();
        productOrderDO.setUserId(loginUser.getId());
        productOrderDO.setHeadImg(loginUser.getHeadImg());
        productOrderDO.setNickname(loginUser.getName());

        //订单信息
        productOrderDO.setOutTradeNo(orderOutTradeNo);
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setDel(0);
        productOrderDO.setOrderType(ProductOrderTypeEnum.DAILY.name());

        //实际支付价格
        productOrderDO.setPayAmount(orderRequest.getTotalPayAmount());
        //总价，未使用优惠券的价格
        productOrderDO.setTotalAmount(orderRequest.getTotalAmount());
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());

        //地址
        productOrderDO.setReceiverAddress(JSON.toJSONString(addressVO));

        productOrderMapper.insert(productOrderDO);

        return productOrderDO;
    }

    /**
     * 锁定库存
     * @param orderItemVOList
     * @param orderOutTradeNo
     */
    private void lockProductStocks(List<OrderItemVO> orderItemVOList, String orderOutTradeNo) {

        List<OrderItemRequest> itemRequestList = orderItemVOList.stream().map(obj->{
            OrderItemRequest request = new OrderItemRequest();
            request.setBuyNum(obj.getBuyNum());
            request.setProductId(obj.getProductId());
            return request;
        }).collect(Collectors.toList());

        LockProductRequest lockProductRequest = new LockProductRequest();
        lockProductRequest.setOrderItemList(itemRequestList);
        lockProductRequest.setOrderOutTradeNo(orderOutTradeNo);

        JsonData jsonData = productFeignService.lockProductStock(lockProductRequest);
        if (jsonData.getCode()!=0){
            log.error("锁定商品库存失败:{}",lockProductRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_LOCK_PRODUCT_FAIL);
        }
    }

    /**
     * 锁定优惠券
     * @param orderRequest
     * @param orderOutTradeNo
     */
    private void lockCouponRecords(ConfirmOrderRequest orderRequest, String orderOutTradeNo) {
        List<Long> lockCouponRecordIds = new ArrayList<>();
        if (orderRequest.getCouponRecordId()>0){
            lockCouponRecordIds.add(orderRequest.getCouponRecordId());
            LockCouponRecordRequest lockCouponRecordRequest = new LockCouponRecordRequest();
            lockCouponRecordRequest.setLockCouponRecordIds(lockCouponRecordIds);
            lockCouponRecordRequest.setOrderOutTradeNo(orderOutTradeNo);

            //发起锁定优惠券请求
            JsonData jsonData = couponFeignService.lockCouponRecords(lockCouponRecordRequest);
            if (jsonData.getCode() != 0){
                throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
            }
        }
    }

    /**
     * 验证价格
     *
     * 1）统计全部商品价格
     * 2）获取优惠券(判断条件)，减去总价
     *
     * @param orderItemVOList
     * @param orderRequest
     */
    private void checkPrice(List<OrderItemVO> orderItemVOList, ConfirmOrderRequest orderRequest) {
        //获取总价
        BigDecimal realPayAmount = new BigDecimal("0");
        if (orderItemVOList != null){
            for (OrderItemVO orderItemVO:orderItemVOList){
                BigDecimal itemRealPayAmount = orderItemVO.getTotalAmount();
                realPayAmount = realPayAmount.add(itemRealPayAmount);
            }
        }

        //获取优惠券，判断是否可以使用
        CouponRecordVO couponRecordVO = getCartCouponRecord(orderRequest.getCouponRecordId());

        //计算购物车价格是否满足优惠券满减条件
        if (couponRecordVO != null){
            if (realPayAmount.compareTo(couponRecordVO.getConditionPrice())<0){
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
            }

            if (couponRecordVO.getPrice().compareTo(realPayAmount)>0){
                realPayAmount = BigDecimal.ZERO;
            } else {
                realPayAmount = realPayAmount.subtract(couponRecordVO.getPrice());
            }
        }

        if (realPayAmount.compareTo(orderRequest.getTotalPayAmount()) !=0){
            log.error("订单验价失败:{}",orderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }
    }

    /**
     * 获取优惠券
     * @param couponRecordId
     * @return
     */
    private CouponRecordVO getCartCouponRecord(Long couponRecordId) {
        if (couponRecordId == null || couponRecordId < 0){
            return null;
        }
        JsonData couponData = couponFeignService.findUserCouponRecordById(couponRecordId);
        if (couponData.getCode() != 0){
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
        }

        if (couponData.getCode() == 0){
            CouponRecordVO couponRecordVO = couponData.getData(new TypeReference<>(){});
            if (!couponAvailable(couponRecordVO)){
                log.error("优惠券使用失败");
                throw new BizException(BizCodeEnum.COUPON_UNAVAILABLE);
            }
            return couponRecordVO;
        }

        return null;
    }

    /**
     * 判断优惠券是否可用
     * @param couponRecordVO
     * @return
     */
    private boolean couponAvailable(CouponRecordVO couponRecordVO) {

        if (couponRecordVO.getUseState().equalsIgnoreCase(CouponStateEnum.NEW.name())){
            long currentTimestamp = CommonUtil.getCurrentTimestamp();
            long end = couponRecordVO.getEndTime().getTime();
            long start = couponRecordVO.getStartTime().getTime();
            if (currentTimestamp>=start && currentTimestamp<=end){
                return true;
            }
        }
        return false;
    }

    /**
     * 获取收获地址详情
     * @param addressId
     * @return
     */
    private ProductOrderAddressVO getUserAddress(Long addressId) {

        JsonData addressData = userFeignService.detail(addressId);

        if (addressData.getCode() != 0){
            log.error("获取收货地址失败");
            throw new BizException(BizCodeEnum.ADDRESS_NO_EXITS);
        }

        ProductOrderAddressVO addressVO = addressData.getData(new TypeReference<>(){});
        log.info("收获地址信息:{}",addressVO);

        return addressVO;
    }


    /**
     * 查询订单状态
     * @param outTradeNo
     * @return
     */
    @Override
    public String queryProductOrderState(String outTradeNo) {
        ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no",outTradeNo));

        if(productOrderDO == null){
            return "";
        }else {
            return productOrderDO.getState();
        }

    }

    /**
     * 定时关单
     * @param orderMessage
     * @return
     */
    @Override
    public boolean closeProductOrder(OrderMessage orderMessage) {
        ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>()
                .eq("out_trade_no",orderMessage.getOutTradeNo()));

        if (productOrderDO == null){
            //订单不存在
            log.warn("直接确认消息，订单不存在:{}",orderMessage);
            return true;
        }
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())){
            //已经支付
            log.info("直接确认消息,订单已经支付:{}",orderMessage);
            return true;
        }
        //向第三方查询是否支付 TODO
        PayInfoVo payInfoVo = new PayInfoVo();
        payInfoVo.setPayType(productOrderDO.getPayType());
        payInfoVo.setOutTradeNo(orderMessage.getOutTradeNo());
        String payResult = payFactory.queryPaySuccess(payInfoVo);

        //结果为空则未支付成功，本地取消订单
        if (StringUtils.isBlank(payResult)){
            productOrderMapper.updateOrderPayState(orderMessage.getOutTradeNo(),ProductOrderStateEnum.CANCEL.name(),ProductOrderStateEnum.NEW.name());
            log.info("结果为空，则未支付成功，本地取消订单:{}",orderMessage);
            return true;
        } else {
            //支付成功，改为已支付
            log.warn("支付成功，主动把订单改成已支付，支付回调有问题:{}",orderMessage);
            productOrderMapper.updateOrderPayState(orderMessage.getOutTradeNo(),ProductOrderStateEnum.PAY.name(),ProductOrderStateEnum.NEW.name());
            return true;
        }

    }

    /**
     * 处理支付结果回调通知
     * @param payType
     * @param paramsMap
     * @return
     */
    @Override
    public JsonData handlerOrderCallbackMsg(String payType, Map<String, String> paramsMap) {

        //量大的话投递到mq里慢慢消费
        if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())){
            //获取商户订单号
            String outTradeNo = paramsMap.get("out_trade_no");
            //交易状态
            String tradeStatus = paramsMap.get("trade_status");
            if ("TRADE_SUCCESS".equalsIgnoreCase(tradeStatus)||"TRADE_FINISHED".equalsIgnoreCase(tradeStatus)){
                //更新订单状态
                productOrderMapper.updateOrderPayState(outTradeNo,ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
                return JsonData.buildSuccess();
            }
        } else if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())){
            //微信支付 TODO
        }
        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
    }

    /**
     * 分页查询我的订单列表
     * @param page
     * @param size
     * @param state
     * @return
     */
    @Override
    public Map<String, Object> page(int page, int size, String state) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        Page<ProductOrderDO> pageInfo = new Page<>(page,size);
        IPage<ProductOrderDO> orderDOIPage = null;

        if (StringUtils.isBlank(state)){
            orderDOIPage = productOrderMapper.selectPage(pageInfo,new QueryWrapper<ProductOrderDO>().eq("user_id",loginUser.getId()));
        } else {
            orderDOIPage = productOrderMapper.selectPage(pageInfo,new QueryWrapper<ProductOrderDO>().eq("user_id",loginUser.getId()).eq("state",state));
        }
        List<ProductOrderDO> productOrderDOList = orderDOIPage.getRecords();
        List<ProductOrderVO> productOrderVOList = productOrderDOList.stream().map(orderDO ->{

            List<ProductOrderItemDO> itemDOList = productOrderItemMapper.selectList(new QueryWrapper<ProductOrderItemDO>().eq("product_order_id",orderDO.getId()));

            List<OrderItemVO> orderItemVOList = itemDOList.stream().map(item->{
                OrderItemVO orderItemVO = new OrderItemVO();
                BeanUtils.copyProperties(item,orderItemVO);
                return orderItemVO;
            }).collect(Collectors.toList());

            ProductOrderVO productOrderVO = new ProductOrderVO();
            BeanUtils.copyProperties(orderDO,productOrderVO);
            productOrderVO.setOrderItemVOList(orderItemVOList);

            return productOrderVO;
        }).collect(Collectors.toList());

        Map<String,Object> pageMap = new HashMap<>();
        pageMap.put("total_record",orderDOIPage.getTotal());
        pageMap.put("total_page",orderDOIPage.getPages());
        pageMap.put("current_data",productOrderVOList);

        return pageMap;
    }

    @Override
    @Transactional
    public JsonData repay(RepayOrderRequest repayOrderRequest) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no",repayOrderRequest.getOutTradeNo())
                .eq("user_id",loginUser.getId()));

        log.info("订单状态:{}",productOrderDO);
        if (productOrderDO == null){
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_NOT_EXIST);
        }

        //订单状态不对，不是NEW状态
        if (!productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())){
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_STATE_ERROR);
        } else {
            //订单创建到现在的存活时间
            long orderLiveTime = CommonUtil.getCurrentTimestamp() - productOrderDO.getCreateTime().getTime();
            orderLiveTime = orderLiveTime + 70*1000;

            if (orderLiveTime>TimeConstant.ORDER_PAY_TIMEOUT_MILLS){
                //订单超时
                return JsonData.buildResult(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT);
            } else {
                //TODO 更新订单支付参数，增加支付信息日志

                //剩下的有效时间
                long timeout = TimeConstant.ORDER_PAY_TIMEOUT_MILLS - orderLiveTime;

                PayInfoVo payInfoVo = new PayInfoVo();
                payInfoVo.setOutTradeNo(productOrderDO.getOutTradeNo());
                payInfoVo.setPayFee(productOrderDO.getPayAmount());
                payInfoVo.setPayType(repayOrderRequest.getPayType());
                payInfoVo.setClientType(repayOrderRequest.getClientType());
                payInfoVo.setTitle(productOrderDO.getOutTradeNo());
                payInfoVo.setDescription("");
                payInfoVo.setOrderPayTimeoutMills(timeout);
                log.error("timeout:{}",timeout);

                log.info("payInfoVO:{}",payInfoVo);
                String payResult = payFactory.pay(payInfoVo);

                if (StringUtils.isNotBlank(payResult)){
                    return JsonData.buildSuccess(payResult);
                } else {
                    log.error("创建支付订单失败：payResult:{},payInfo:{}",payResult,payInfoVo);
                    return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
                }
            }
        }

    }
}
